﻿#ifndef LK_WINDOWS_WAS_SPEAKER_///由W意波(LuoJun)编写
#define LK_WINDOWS_WAS_SPEAKER_
namespace LK
{
	namespace Windows
	{
		namespace WAS
		{
			using namespace ::LK::Sound;
			//扬声器类，每个类关联一个音频会话
			class Speaker :public Object
			{
				//线程同步控制
				Mutex tc;
				//音频格式转换
				Wave converter;
				//数据缓冲区
				SoundBuffer buffer;

				//代表指定的设备
				IMMDevice *pDevice;
				//音频会话
				IAudioClient *pAudioClient;
				//音频播放客户端
				IAudioRenderClient *pRenderClient;
				//按时间请求音频块大小，10000000分之1秒为单位
				PTime hnsRequestedDuration;
				//设备块总帧数
				UINT32 bufferFrameCount;
				//声音频率，常见的有8000，16000，22500，44100，48000，96000等
				double hz;
				//调用的线程数
				index_t threadCount;
				//声道数，一般为2
				short ch;
				//比特数，一般为16或32
				short bits;
				//是否正在写入
				bool isWriting;
				//是否为浮点
				bool isFloat;
				//是否正在播放
				bool isPlaying;
				//是否已Start
				bool started;
				//是否正在选择设备
				bool isSelecting;
				//是否正在关闭
				bool isClosing;
				//写入指定内存区域的len长度的数据
				void WriteByte(u8*p, size_t len);

			public:

				BMETHODS(PSpeaker,Speaker)
				/**
				@brief 赋值语句将抛出异常*/
				Speaker& operator=(const Speaker&) { throw NotSupportException(); }
				/**
				@brief 复制将抛出异常*/
				Speaker(const Speaker&) { throw NotSupportException(); }
				//是否使用默认设备
				bool UsingDefaultDevice;
				//混音格式，（蛋疼）
				WAVEFORMATEX SpeakingFormat;
				//当前使用的设备ID
				PString DeviceID;
				//当前使用的设备名称
				PString DeviceName;
				/**
				@brief 构造函数，启动一个音频回话并关联默认的扬声设备*/
				Speaker();
				/**
				@brief 析构函数，关闭音频会话*/
				virtual ~Speaker();
				/**
				@brief 设置音量大小
				@param [传入]参数vol为放大倍数，一般取0~1，大于1可能会爆破）*/
				void SetVol(double vol);
				/**
				@brief 获取当前音量大小（一般为0~1）*/
				double GetVol()const;
				/**
				@brief 开始或继续播放缓冲的数据，该方法应属于异步操作，线程安全*/
				void Start();
				/**
				@brief 停止播放,清空缓冲数据，该方法应属于异步操作，线程安全*/
				void Stop();
				/**
				@brief 暂停播放，不清除缓存，该方法应属于异步操作，线程安全*/
				void Pause();
				/**
				@brief 关闭会话*/
				void Close();
				/**
				@brief 清空缓存内容*/
				void Clear();
				/**
				@brief 写入PCM音频数据,不完成不返回。该操作方法属同步操作*/
				template<typename T>void Write(const LK::Collection::PArray<T>&xArray, index_t startIndex, index_t length)
				{
					if (length + startIndex > xArray.size())throw PIndexOutOfRangeException();
					WriteByte((u8*)(&xArray[startIndex]), length*sizeof(T));
				}
				/**
				@brief 写入PCM音频数据,不完成不返回。该操作方法属同步操作*/
				template<typename T>void Write(const LK::Collection::PArray<T>&xArray, index_t startIndex = 0){ return Write(xArray, startIndex, xArray.size() - startIndex); }
				/**
				@brief 写入PCM音频数据,不完成不返回。该操作方法属同步操作*/
				template<typename T>void Write(const LK::Collection::DefaultArray<T>&xArray, index_t startIndex, index_t length){ return Write(LK::Collection::PArray<T>(xArray.size(), xArray, Reference), startIndex, length); }
				/**
				@brief 写入PCM音频数据,不完成不返回。该操作方法属同步操作*/
				template<typename T>void Write(const LK::Collection::DefaultArray<T>&xArray, index_t startIndex = 0){ return Write(LK::Collection::PArray<T>(xArray.size(), xArray, Reference), startIndex); }
				/**
				@brief 写入一个波形数据，不完成不返回,注意该操作将改变输入音频格式设置*/
				void Write(const WaveData& data){ SetFormat(data.Hz, data.Bits, data.Channels, data.IsFloat); Write(LK::Collection::PArray<unsigned char>(new LK::Collection::ReferenceArray<unsigned char>(data.Data.size(), data.Data))); }
				/**
				@brief 写入一个波形数据，不完成不返回,注意该操作将改变输入音频格式设置*/
				void Write(const PWaveData& data){ if (data.IsNull())return; SetFormat(data.GetHz(), data.GetBits(), data.GetChannels(), data.GetIsFloat()); Write(LK::Collection::PArray<unsigned char>(new LK::Collection::ReferenceArray<unsigned char>(data.GetData().size(), data.GetData()))); }
				/**
				@brief 设置缓存时间长度，以100ns为单位*/
				void SetBufferTime(const PTime& hms);
				/**
				@brief 设置播放的PCM音频格式，isfloat指示是否为浮点*/
				void SetFormat(double h, short bit, short channel, bool isfloat = false);
				/**
				@brief 设置播放的PCM音频格式（使用系统的PCM格式）*/
				void SetFormat(const WAVEFORMATEX& fwx);
				/**
				@brief 设置默认的PCM音频格式*/
				void SetToDefaultFormat();
				/**
				@brief 获取当前的PCM音频格式，如果只想获取一个属性，其他参数可以传入NULL*/
				void GetFormat(index_t* phz, short* pbits, short* pch, bool *isfloat = NULL);
				/**
				@brief 获取当前的PCM音频格式*/
				WAVEFORMATEX GetFormat();
				/**
				@brief 获取播放设备的Id列表*/
				static LK::Collection::PArray<PString> GetIdList();
				/**
				@brief 获取播放设备的名称列表*/
				static LK::Collection::PArray<PString> GetNameList();
				/**
				@brief 按设备Id重新选择设备，成功返回true*/
				bool SelectDeviceById(const PString& Id);
				/**
				@brief 按设备名重新选择设备，成功返回true*/
				bool SelectDeviceByName(const PString& name);
				/**
				@brief 设备更改时重启*/
				void Restart();
				/**
				@brief 设备添加事件*/
				void OnDeviceAdded();
				/**
				@brief 设备状态改变事件*/
				void OnDeviceStateChanged();
				/**
				@brief 设备移除事件*/
				void OnDeviceRemoved();
				/**
				@brief 默认设备改变事件*/
				void OnDefaultDeviceChanged();
				/**
				@brief 设备属性改变事件*/
				void OnDevicePropertyValueChanged();
			};
			class PSpeaker :public PObject
			{
			public:
				PMETHODS(PSpeaker, Speaker);
				/**
				@brief 使用音量大小来创建一个新扬声器*/
				PSpeaker(double vol) :PObject(new Speaker()){ SetVol(vol); }
				/**
				@brief 使用音量值、采样率、声道数、采样位数来创建新的扬声器*/
				PSpeaker(double vol, double hz, short bits, short ch, bool isFloat = false) :PObject(new Speaker())
				{
					SetVol(vol); SetFormat(hz, bits, ch, isFloat);
				}
				/**
				@brief 设置音量大小（vol为放大倍数，一般取0~1，大于1可能会爆破）*/
				void SetVol(double vol){ return Ptr()->SetVol(vol); }
				/**
				@brief 获取当前音量大小（一般为0~1）*/
				double GetVol()const{ return Ptr()->GetVol(); }
				/**
				@brief 获取一个bool值，指示是否使用默认设备*/
				bool GetUsingDefaultDevice()const{ return Ptr()->UsingDefaultDevice; }
				/**
				@brief 设置一个bool值，指示是否使用默认设备*/
				void SetUsingDefaultDevice(bool b){ Ptr()->UsingDefaultDevice = b; }
				/**
				@brief 获取当前格式*/
				WAVEFORMATEX GetSpeakingFormat()const{ return Ptr()->SpeakingFormat; }
				/**
				@brief 设置当前格式*/
				void SetSpeakingFormat(WAVEFORMATEX x){ Ptr()->SpeakingFormat = x; }
				/**
				@brief 获取当前设备ID*/
				PString GetCurrentDeviceID()const{ return Ptr()->DeviceID; }
				/**
				@brief 获取当前设备名*/
				PString GetCurrentDeviceName()const{ return Ptr()->DeviceName; }
				/**
				@brief 开始或继续播放缓冲的数据，该方法应属于异步操作*/
				void Start(){ return Ptr()->Start(); }
				/**
				@brief 停止播放,清空缓冲数据，该方法应属于异步操作*/
				void Stop(){ return Ptr()->Stop(); }
				/**
				@brief 暂停播放，不清除缓存，该方法应属于异步操作*/
				void Pause(){ return Ptr()->Pause(); }
				/**
				@brief 关闭会话*/
				void Close(){ return Ptr()->Close(); }
				/**
				@brief 清空缓存内容*/
				void Clear(){ return Ptr()->Clear(); }
				/**
				@brief 写入PCM音频数据,不完成不返回。该操作方法属同步操作*/
				template<typename T>void Write(const LK::Collection::PArray<T>&xArray, index_t startIndex = 0){ return Ptr()->Write(xArray, startIndex, xArray.size() - startIndex); }
				/**
				@brief 写入PCM音频数据,不完成不返回。该操作方法属同步操作*/
				template<typename T>void Write(const LK::Collection::PArray<T>&xArray, index_t startIndex, index_t length){ return Ptr()->Write(xArray, startIndex, length); }
				/**
				@brief 写入一个波形数据，不完成不返回,注意该操作将改变输入音频格式设置*/
				void Write(const WaveData& data){ return Ptr()->Write(data); }
				/**
				@brief 写入一个波形数据，不完成不返回,注意该操作将改变输入音频格式设置*/
				void Write(const PWaveData& data){ return Ptr()->Write(data); }
				/**
				@brief 设置缓存时间长度，以100ns为单位*/
				void SetBufferTime(const PTime& hms){ return Ptr()->SetBufferTime(hms); }
				/**
				@brief 设置数据的PCM音频格式，isfloat指示是否为浮点*/
				void SetFormat(double hz, short bits, short ch, bool isFloat = false){ return Ptr()->SetFormat(hz, bits, ch, isFloat); }
				/**
				@brief 设置数据的PCM音频格式*/
				void SetFormat(const WAVEFORMATEX& pwfx){ return Ptr()->SetFormat(pwfx); }
				/**
				@brief 设置默认的PCM音频格式*/
				void SetToDefaultFormat(){ return Ptr()->SetToDefaultFormat(); }
				/**
				@brief 获取当前设置的数据PCM音频格式，如果只想获取一个属性，其他参数可以传入NULL*/
				void GetFormat(index_t* phz, short* pbits, short* pch, bool *isFloat = NULL){ return Ptr()->GetFormat(phz, pbits, pch, isFloat); }
				/**
				@brief 获取当前的PCM音频格式*/
				WAVEFORMATEX GetFormat(){ return Ptr()->GetFormat(); }
				/**
				@brief 获取播放设备的Id列表*/
				static LK::Collection::PArray<PString> GetIdList(){ return Speaker::GetIdList(); }
				/**
				@brief 获取播放设备的名称列表*/
				static LK::Collection::PArray<PString> GetNameList(){ return Speaker::GetNameList(); }
				/**
				@brief 按设备Id重新选择设备，成功返回true*/
				bool SelectDeviceById(const PString& Id){ return Ptr()->SelectDeviceById(Id); }
				/**
				@brief 按设备名重新选择设备，成功返回true*/
				bool SelectDeviceByName(const PString& name){ return Ptr()->SelectDeviceByName(name); }
				/**
				@brief 设备更改时重启*/
				void Restart(){ return Ptr()->Restart(); }
			};

		}
	}
}
#endif